package de.hbrs.selectionengine.bean;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import javax.ejb.Stateless;

import de.hbrs.selectionengine.model.BPELProcess;
import de.hbrs.selectionengine.model.Operation;
import de.hbrs.selectionengine.model.Param;
import de.hbrs.selectionengine.model.WebService;
import de.hbrs.selectionengine.tool.AntInvoker;
import de.hbrs.selectionengine.tool.IOHelper;
import de.hbrs.selectionengine.tool.URLFileSaver;
import de.hbrs.selectionengine.tool.XMLPreparation;

@Stateless
public class WebServiceProxyGenerationBean {

	private BPELProcess bpelProcess;
	private WebService webservice;
	private String workingFolderPath;
	private String fileSeperator;
	private String serviceName;
	private File wsdlFile;
	private String wsdlFQN;
	private String targetWsdlName;
	private String targetWsdlPath;
	private File xsdFile;
	private String targetXsdPath;
	private String targetXsdName;
	private String targetSoapAddress;
	private String wsImportFQN;
	private String namespaceWSProxy;
	private String namespaceISelectionEngine;
	private String selectionEngineInterface;
	private String selectionEngineJNDILookupName;

	public WebServiceProxyGenerationBean() {

	}

	public WebServiceProxyGenerationBean(BPELProcess bpelProcess,
			String workingFolderPath, String targetSoapAddress,
			String wsImportFQN, String namespaceWSProxy,
			String namespaceISelectionEngine, String selectionEngineInterface,
			String selectionEngineJNDILookupName) {

		this.bpelProcess = bpelProcess;
		this.workingFolderPath = workingFolderPath;
		this.wsImportFQN = wsImportFQN;
		this.targetSoapAddress = targetSoapAddress;
		this.namespaceWSProxy = namespaceWSProxy;
		this.namespaceISelectionEngine = namespaceISelectionEngine;
		this.selectionEngineInterface = selectionEngineInterface;
		this.selectionEngineJNDILookupName = selectionEngineJNDILookupName;
		

	}

	public void createWebServiceProxies() {

		//For each process step in the BPEL process
		for (int i = 0; i < bpelProcess.getProcessSteps().size(); i++) {

			this.webservice = this.bpelProcess.getProcessSteps().get(i)
					.getWebservice();
					
			this.wsdlFQN = this.webservice.getWsdllocation();
			this.serviceName = String.valueOf(this.webservice.getName());
			this.targetWsdlName = this.webservice.getName() + ".wsdl";
			this.targetXsdName = this.webservice.getName() + ".xsd";
			this.fileSeperator = System.getProperty("file.separator");
			this.targetWsdlPath = this.workingFolderPath + this.fileSeperator
					+ this.serviceName + this.fileSeperator + "WebContent"
					+ this.fileSeperator + "WEB-INF" + this.fileSeperator
					+ "wsdl";
			this.targetXsdPath = this.workingFolderPath + this.fileSeperator
					+ this.serviceName + this.fileSeperator + "WebContent"
					+ this.fileSeperator + "WEB-INF" + this.fileSeperator
					+ "wsdl";

			this.createWorkingDirectory();
			this.saveWSDLFile();
			//Not needed in jboss unless there is an complex datatype stored in an separate xsd file.
			//TODO insert distinction of cases for the case that the complex datatyps are present in the WSDL or in an seperate xsd file.
			// this.saveXSDFile();
			this.changeAddresses();
			this.callWsImport();
			this.createSelectionEngineInterface();
			this.createWSProxyImpl();
			this.callAntFile();
		}
	}

	private void callAntFile() {
		AntInvoker ai = new AntInvoker(new File(System.getProperty("user.dir")
				+ this.fileSeperator + "build.xml"));
		ai.changeProperty("webservice.name", this.serviceName);
		ai.callDefaultAntTask();
	}

	private String createWSProxyImpl() {

		StringBuilder sb = new StringBuilder();

		sb.append("package " + namespaceWSProxy + "; \n\n");
		sb.append("import javax.jws.WebMethod; \n");
		sb.append("import javax.jws.WebService; \n");
		sb.append("import javax.naming.InitialContext; \n");
		sb.append("import javax.naming.NamingException; \n");
		sb.append("import java.lang.*; \n");
		sb.append("import java.io.Serializable; \n");
		sb.append("import " + this.namespaceWSProxy + "." + this.serviceName
				+ ";\n");
		sb.append("import " + this.namespaceISelectionEngine
				+ ".ISelectionEngine; \n\n");
		sb.append("@WebService \n");
		sb.append("public class " + serviceName
				+ "Impl implements Serializable, " + this.serviceName + " { \n");
		sb.append("@WebMethod \n");

		Operation op = this.webservice.getOperation();

		sb.append("public " + op.getOutput().getType() + " " + op.getName()
				+ "(");

		List<Param> args = op.getInput();

		for (int i = 0; i <= args.size() - 1; i++) {

			Param p = args.get(i);

			sb.append(p.getType() + " " + p.getName());

			if (i != args.size() - 1)
				sb.append(", ");
		}

		sb.append(") { \n");

		sb.append("try {\n");
		sb.append("InitialContext ic = new InitialContext();\n");
		sb.append("ISelectionEngine se = (ISelectionEngine) ic.lookup(\"");
		sb.append(this.selectionEngineJNDILookupName + "\");\n");

		sb.append("return ");
		sb.append(getAppropirateReturnValue(op.getOutput().getType()));
		sb.append("se.callWebService(" + this.bpelProcess.getId() + ", " + this.webservice.getId()
				+ ", new Object[] {");

		for (int i = 0; i <= args.size() - 1; i++) {

			sb.append(args.get(i).getName());

			if (i != args.size() - 1)
				sb.append(", ");
		}

		sb.append("}).toString()); \n");
		sb.append("}");
		sb.append("catch (NamingException e) {\n");
		sb.append("e.printStackTrace(); } \n");
		sb.append("return null; } \n");
		sb.append("}");

		String namespace = namespaceWSProxy.replaceAll("\\.+", fileSeperator
				+ fileSeperator);
		String fileName = workingFolderPath + fileSeperator + this.serviceName
				+ fileSeperator + "src" + fileSeperator + namespace
				+ this.fileSeperator + this.serviceName + "Impl.java";

		File wspImpl = new File(fileName);

		try {
			FileWriter fw = new FileWriter(wspImpl, false);

			fw.write(sb.toString());

			fw.flush();
			fw.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
		return sb.toString();
	}

	private String getAppropirateReturnValue(String type) {

		switch (type) {
		case "boolean":
			return "Boolean.valueOf(";
		case "double":
			return "Double.valueOf(";
		case "float":
			return "Float.valueOf(";
		case "integer":
			return "Integer.valueOf(";
		case "long":
			return "Long.valueOf(";
		case "short":
			return "Short.valueOf(";
		case "String":
			return "String.valueOf(";
		case "byte":
			return "Byte.valueOf(";
		case "character":
			return "Character.valueOf(";
		default:
			return type;
		}
	}

	private void createSelectionEngineInterface() {

		String namespace = this.namespaceISelectionEngine.replaceAll("\\.+",
				fileSeperator + this.fileSeperator);
		String fileName = workingFolderPath + this.fileSeperator + serviceName
				+ this.fileSeperator + "src" + this.fileSeperator + namespace
				+ this.fileSeperator + "ISelectionEngine.java";

		String seInterface = this.selectionEngineInterface;
		try {
			FileWriter fw = new FileWriter(fileName, false);

			fw.write(seInterface);

			fw.flush();
			fw.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void callWsImport() {
		try {
			// WSImport Parameter:
			// http://jax-ws.java.net/nonav/2.2.1/docs/wsimport.html
			String argus[] = {
					this.wsImportFQN,
					"-p",
					namespaceWSProxy,
					"-s",
					workingFolderPath + fileSeperator + serviceName
							+ fileSeperator + "src",
					"-d",
					workingFolderPath + fileSeperator + serviceName
							+ fileSeperator + "WebContent" + this.fileSeperator
							+ "WEB-INF" + fileSeperator + "classes",
					this.targetWsdlPath + this.fileSeperator
							+ this.targetWsdlName };

			IOHelper.callCommandLineTool(argus);

		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void changeAddresses() {
		XMLPreparation.setSOAPAddress(this.wsdlFile, targetSoapAddress);
	}

	private void saveXSDFile() {
		this.xsdFile = URLFileSaver.saveXML(
				XMLPreparation.getXSDPathFromWSDL(this.wsdlFile),
				this.targetXsdPath + this.fileSeperator + this.targetXsdName);
	}

	private void saveWSDLFile() {
		this.wsdlFile = URLFileSaver.saveXML(this.wsdlFQN, this.targetWsdlPath
				+ this.fileSeperator + this.targetWsdlName);
	}

	private void createWorkingDirectory() {
		try {
			IOHelper.createFolders(workingFolderPath + this.fileSeperator
					+ this.serviceName + this.fileSeperator + "WebContent"
					+ this.fileSeperator + "WEB-INF" + this.fileSeperator
					+ "wsdl");

			IOHelper.createFolders(workingFolderPath + this.fileSeperator
					+ this.serviceName + this.fileSeperator + "WebContent"
					+ this.fileSeperator + "WEB-INF" + this.fileSeperator
					+ "classes");

			IOHelper.createFolders(workingFolderPath + this.fileSeperator
					+ this.serviceName + this.fileSeperator + "src");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
